#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <pthread.h>
#include "logger.h"
#include "sem.h"
#include "include.h"

#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE

struct memory opmem, swapmem;

struct data1 *op;
size_t op_n;

struct data0 *swap;
size_t swap_n;

size_t next;

//HINT: Max, my comments are marked as (a)

//(a)add threads: it will make your program faster! there are no threads in my program, but I'll add them, too
//(a)make poisoning: it is a symbol of good taste:) you may understand the idea of poisoning in my program: one function, used, but under comments yet, in init and free as I remember

//(a)your mmap function may work incorrectly as it's connected with page size (4kb), so you should allign to page_size
//-----------------------------------------------------------------------------------------
void display(){//(a)in fact, in the release version of our program should be ONLY 6 functions, other should be moved to another file or marked as static
	//(a)static means that our beloved user can't use it: they can be used only in the file where they are defined and are not seen in other places
	//(a)and I'll repeat that that's a good practice to make more than one file: in each file there should be separate logical chunk; Anna said it, so we should follow her advice IMHO. (I'll do that later with my program=))
	//(a)you made two file, but there is not so much logic in this division. But maybe I cant see logic because not I wrote this program:)
	FILE * f = fopen(MEM, "w+");
	fprintf(f,"op_mem:\n");
	size_t i = 0;
	for(; i < op_n; i++) fprintf(f,"id - %d , %d, %d;\n", op[i].n, op[i].sz, op[i].sw);//(a)that cuts my eyes: is it ok to use for and its body on one line? (I'm not sure if that's ok or not)
	//(a)for (_; i<n; i++)-very strange construction. maybe it works, but I suppose that there is no action in the cicle, written in this form, so that we can ommit it and it's useless
	fprintf(f,"swap_mem:\n");
	for(i = 0 ; i < swap_n; i++) fprintf(f,"id - %d , %d;\n", swap[i].n, swap[i].sz);
	fclose(f);
}
//-----------------------------------------------------------------------------------------

int ma_init(size_t mem, off_t sw, const char* swap_path){
	init_logger();
	if( swapmem.st && opmem.st && op && swap) {(a)//I told you that in my opinion one variable for initialisation is more understandable, but that's only my opinion=) 
		errno = EALREADY;
		log(0);
		return 0;
		}
		
	if( mem > sw){
		errno = EBADR;
		log(0);
		return 0;
		}

	int fd = open(swap_path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR); 
	if(!fd){ 
		log(0);
		return 0;
		}//(a)move this bracket on one tab to the left: that's not proper coding style=) and in many other places the same.

	lseek (fd, sw + 1, SEEK_SET); 
	write (fd, "", 1);
	lseek (fd, 0, SEEK_SET);//(a)isn't ftruncate better?

	swapmem.st = mmap (0, sw, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	swapmem.n = sw;
	close(fd);

	opmem.st = malloc(mem);

	//(a)you allocate memory in one place and mmap to another random (0-first argument) place. they are not connected!!! (but maybe I misunderstood something:))

	if( !opmem.st) {
			errno = ENOMEM;
			log(0);
			return 0;
			}
	opmem.n = mem;
	next = 1;

	op = (struct data1 *)malloc(sizeof(struct data1));
	op[0].n = 0;	
	op[0].sz = mem;
	op_n = 1;

	swap = (struct data0 *)malloc(sizeof(struct data0));
	swap[0].n = 0;	
	swap[0].sz = sw;
	swap_n = 1;
		
	//sem_init();
	
	log(1);//(a) you've written log with caps in file logger.h and here it is written with little letters!!!
	return 1;
};

/*
EALREADY - повторный вызов ma_init
ENOMEM - невозможно выделить место под кучу размеров mem +
EACCES - нет прав доступа к файлу swap_path
EFAULT - адрес swap_path недоступен
EFBIG - запрашиваемый размер файла подкачки слишком большой
EBADR - mem>swap
*/

//------------------------------------------------------------------------

void ma_deinit(){
	//pre2end();
 	free(op);
	free(swap);
	free(opmem.st); 
	munmap (swapmem.st, swapmem.n);
	log(1);
	//sem_deinit();
	};

//--------------------------------------------------------------------------
int ma_free(size_t id){ 
	size_t i = 0;
	int k;
	if( !id) return 1;

	if( !(swapmem.st && opmem.st && op && swap)) {
		errno = ECANCELED;
		log(0);
		return 0;
		}
	
	//wait(1);
	for(; i < swap_n; i++) if( swap[i].n == id) break;//(a) again: 1) strange cicle; 2) for, if and body on one line!!! not good
	if( i != swap_n) {
		mem_free0(swap, swap_n, i, swapmem);  
		data_free0(swap, swap_n, i);
		log(1);
		display();//(a)do we need it?
		//done(1);
		return 1;
	}
	//done(1);
	
	//wait(0);
	for(i = 0; i < op_n; i++) if( op[i].n == id) break;
	if ( i == op_n) {//(a) here you check that the memory was never allocated for this id, but it seems to me (I'm not sure) that you haven't checked that it was freed or released(which is, again, EFAULT)
		errno = EFAULT;
		log(0);
		//done(0); 
		return 0;
		}
	
	mem_free1(op, op_n,i, opmem);
	data_free1(op,op_n,i);
	log(1);
	display();
	//done(0);
	return 1;
};//(a) you haven't checked EBUSY!!! incorrect work of you program! firstly each chunk of memory should be released and ONLY after that it can be freed, caaording to our TZ

/*
EFAULT - память с таким идентификатором ещё не выделена, или уже освобождена
EBUSY - память ещё не освобождена
ECANCELED - вызов функции до инициализации аллокатора
*/

//-------------------------------------------------------------------------------------------

void do_swap(){ 
	size_t z = 0, i, a, k = 0;	

	z = sum_before0(swap, swap_n);
	for(i = 0; i < op_n; i++) {	
		if(op[i].sw){
			printf("2");
			if( z + op[i].sz > swapmem.n ) break;
			swap_n = swap_add(op[i], swap, swap_n); //fixme
			a = sum_before1(op, i);
			memcpy(opmem.st+a-1, swapmem.st+z-1, op[i].sz); 
			z += op[i].sz;
			mem_free1(op,op_n,i,opmem);
			op_n = data_free1(op,op_n,i);
			}
		else i++;
			}					
	}; 

size_t op_add( size_t i, size_t sz){ 
	if( op[op_n-1].sz < sz || op[op_n-1].n){//(a) op[ap_n-1].sz?? maybe op[ap_n-1]->size?
		//wait(1);
		if(swap[swap_n-1].n) {
			//done(1);
			return 0;
			}
		printf("1");
		do_swap();
		//done(1);
		if(op[op_n-1].sz < sz || op[op_n-1].n){//(a) again -> instead of .
			return 0;
			}
		}

	op[op_n-1].sz -= sz;//(a) again -> instead of .
	if( op[op_n-1].sz){//(a) again -> instead of .
		struct data1 *one;
		one = (struct data1*)malloc((op_n+1)*sizeof(struct data1));
		memcpy(one,op,op_n*sizeof(struct data1));
		one[op_n].sz = op[op_n-1].sz;//(a) again -> instead of .
		one[op_n].n = 0;//(a) again -> instead of .
		one[op_n-1].n = i;//(a) again -> instead of .
		one[op_n-1].sz = sz;//(a) again -> instead of .
		one[op_n-1].sw = 0;//(a) again -> instead of .
		free(op);
		op = one;

		op_n++;
		}
	else{
	 	op[op_n-1].n = i;//(a) again -> instead of .
		op[op_n-1].sz = sz;//(a) again -> instead of .
		op[op_n-1].sw = 0;//(a) again -> instead of .
		}
	return 1;
};
	
size_t ma_alloc(size_t sz){
	if( !sz) return 1;
	if( !(swapmem.st && opmem.st && op && swap)) {//(a) and here it's correct(., not ->)
		errno = ECANCELED;
		log(0);
		return 0;
		}

	if( sz > opmem.n){
		errno = E2BIG;
		log(0);
		return 0;
		}

	if( next == 0) {//(a) have you checked it correctly? where in your program and how can it become 0? you should check that it's smaller than the maximum size of the variable size_t
		errno = EMLINK;
		log(0);
		return 0;
		}
	//done(0);
	int i;
	i = op_add(next,sz);
	if(!i) {
		errno = ENOMEM;
		log(0);
		//done(0);
		return 0;
		}
	log(1);
	next++; 
	display();
	//done(0);
	return next-1;      		 
	};


/*
ENOMEM - в куче недостаточно памяти для выделения участка размером sz (swap - уже_выделенная_память < sz)
E2BIG - запрашиваемый участок памяти невозможно будет уместить в оперативной памяти (mem < sz)
EMLINK - закончились свободные идентификаторы
ECANCELED - вызов функции до инициализации аллокатора
 */

//-------------------------------------------------------------------

void* ma_get(size_t id){
	if( !id) return NULL;
	if( !(swapmem.st && opmem.st && op && swap)) {
		errno = ECANCELED;
		log(0);
		return NULL;
		}

	size_t i = 0, b;
	off_t k, t;
	void * a;
	for(; i < swap_n; i++) if( swap[i].n == id) break;//(a) again: 1) strange cicle; 2) for, if and body on one line!!! not good
	if( i != swap_n) {
		b = op_add(id, swap[i].sz);//(a) again -> instead of .

		if(!b) {
			log(0);
			return NULL;
			}

		k = sum_before0(swap,i);
		if( op[op_n-1].n ) t = sum_before1(op,op_n-1);//(a) again -> instead of .
		else t = sum_before1(op,op_n-2);
		memcpy(opmem.st+t-1,swapmem.st+k-1,swap[i].sz);

		mem_free0(swap, swap_n, i, swapmem);  
		data_free0(swap, swap_n, i);			
		log(1);
		
		return opmem.st+t-1;
	}

	for(i = 0; i < op_n; i++) if( op[i].n == id) break;//(a) again:  for, if and body on one line!!! not good
	if ( i == op_n) {
		errno = EFAULT;
		log(0); 
		return NULL;
		}
	
	k = sum_before1(op,i);
	log(1);
	return opmem.st+k-1;
	};//(a)ENOMEM, EBADF, EFBIG not checked!
/*(a) it seems to me that you have the wrong understanding of why do we need mmap. You mmap 1 time or even want to mmap (because it is mmaped not on our memory of allocator but on random place, so you do it incorrectly logically) 
the whole allocator on file! and why do we need the swap file in such a case? only part of swap file is mmaped to our memory, and other part is hanging useless somewhere, which leads to the incorrect job of the whole program.
We need mmap for the next thing: to place all the data, written by user in the swap file without long routine of copying it to the buf and from the buf to the file. It's just written.
1). in function get, if not mmaped yet, we mmap the chunk of the file (and the first argument is the location of it in our allocator) to this chunk in allocator, and, so, the user, while writing, writes at the same time to allocator and file.
2). in function release, when we want to remove the chunk from the allocator memory, we just do munmap and this chunk is only in the swap file after that.
*/




/*
EFAULT - память с таким идентификатором ещё не выделена, или уже освобождена
ENOMEM - недостаточно места в оперативной памяти
ENOBUFS - память слишком фрагментирована

EBADF - ошибка файла подкачки
ECANCELED - вызов функции до инициализации аллокатора
*/

//--------------------------------------------------------------------------------

int ma_release(size_t id){
	if( !id) return 1;
	if( !(swapmem.st && opmem.st && op && swap)) {
		errno = ECANCELED;
		log(0);
		return 0;
		}

	size_t i = 0;

	for(; i < op_n; i++) if( op[i].n == id) break;//(a) again: 1) strange cicle; 2) for, if and body on one line!!! not good
	if ( i == op_n) {
		errno = EFAULT;
		log(0); 
		return 0;
		}
	if( i != op_n && op[i].sw == 1 ){
		errno = EFAULT;
		log(0); 
		return 0;
		}

	op[i].sw = 1;//(a) again -> instead of 
	log(1);
	display();
	return 1;	 
};

/*
EFAULT - память с таким идентификатором ещё не выделена, или уже освобождена(с помощью ma_free() или ma_release())
ECANCELED - вызов функции до инициализации аллокатора
*/
